﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using EasyComm.Base.Hosting;
using EasyComm.Contracts;
using EasyComm.Discovery;
using EasyComm.ServiceUnit;
using EasyComm.Utilities;

namespace EasyComm.Hosting.Administration
{
    public class SLBAdminService : IAdminSLBContract
    {
        private ProxyServiceHostBase _proxyHost;
        private List<Type> _serviceContractList = new List<Type>();
        private Uri _nlbProxyUrl;
        public AdminServiceUnitCollection<IAdminWorkerContract> WorkerAdminServiceList { get; private set; }
        internal AdminServiceUnitCollection<IAdminWorkerContract> _workerAdminServiceList { get; private set; }

        public SLBAdminService()
        {
            WorkerAdminServiceList = new AdminServiceUnitCollection<IAdminWorkerContract>();
            _workerAdminServiceList = new AdminServiceUnitCollection<IAdminWorkerContract>();
            _workerAdminServiceList.Change += new ThreadSafeCollectionChangeEventHandler<AdminServiceUnit<IAdminWorkerContract>>(_workerAdminServiceList_Change);
        }

        void _workerAdminServiceList_Change(object sender, ThreadSafeCollectionEventArgs<AdminServiceUnit<IAdminWorkerContract>> e)
        {
            switch (e.ChangeType)
            {
                case ChangeType.NotChanged:
                    break;
                case ChangeType.Added:
                    WorkerAdminServiceList.Insert(e.Index, e.Item);
                    break;
                case ChangeType.Removed:
                    WorkerAdminServiceList.Remove(e.Item);
                    break;
                case ChangeType.Modified:
                    WorkerAdminServiceList[e.Index] = e.Item;
                    break;
                case ChangeType.RemovedAll:
                    WorkerAdminServiceList.Clear();
                    break;
                default:
                    break;
            }
        }

        public ServiceResponse Initialize(Uri nlbProxyUrl)
        {
            try
            {
                _nlbProxyUrl = nlbProxyUrl;
                string contractFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Contracts");
                if (!Directory.Exists(contractFolderPath))
                {
                    return new ServiceResponse(1, string.Format("No contracts found on '{0}'.", contractFolderPath), MessageType.Warn);
                }
                string[] contractAssemblyList = Directory.GetFiles(contractFolderPath, "*.dll");
                foreach (string filePath in contractAssemblyList)
                {
                    List<Type> contracts = ReflectionContext.FindContractInterfaces(filePath);
                    if (contracts.Count > 0)
                    {
                        _serviceContractList.AddRange(contracts);
                    }
                }
                if (_serviceContractList.Count > 0)
                {
                    return RestartServices();
                }
                else
                {
                    return new ServiceResponse(1, "Assembly does not implement any contracts.", MessageType.Warn);
                }
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        public ServiceResponse InitializeContracts(Uri nlbProxyUrl, Type[] serviceContractList)
        {
            try
            {
                _nlbProxyUrl = nlbProxyUrl;
                if (serviceContractList != null && serviceContractList.Length > 0)
                {
                    _serviceContractList.AddRange(serviceContractList);
                }
                else
                {
                    return new ServiceResponse(2, "serviceContractList is null or not an object!", MessageType.Error);
                }
                return RestartServices();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        public SystemServiceUnit GetProxyService()
        {
            SystemServiceUnit service = null;
            service.Status = ServiceStatus.NA;
            if (_proxyHost != null)
            {
                service = new SystemServiceUnit(_proxyHost) { Type = SystemServiceType.SLBProxy };
                if (_proxyHost.State == System.ServiceModel.CommunicationState.Opened)
                {
                    service.Status = ServiceStatus.Online;
                }
                else
                {
                    service.Status = ServiceStatus.Offline;
                }
            }
            return service;
        }

        public ServiceResponse SetWorkerServices(int count)
        {
            throw new NotImplementedException();
        }

        public ServiceResponse LoadWorkerServiceAssembly(byte[] file, string fileName)
        {
            try
            {
                string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Contracts", fileName);
                FileUtils.SaveFile(file, filePath);
                List<Type> contracts = ReflectionContext.FindContractInterfaces(filePath);
                if (contracts.Count > 0)
                {
                    _serviceContractList.AddRange(contracts);
                }
                else
                {
                    return new ServiceResponse(1, "Assembly does not implement any contracts.", MessageType.Warn);
                }
                return RestartServices();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse StopProxyService()
        {
            try
            {
                if (_proxyHost != null)
                {
                    _proxyHost.Close();
                    _proxyHost = null;
                }
                Trace.WriteLine("SLB DiscoveryProxy stopped.");
                Trace.WriteLine("");
                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse SetProxyService()
        {
            try
            {
                if (_serviceContractList == null || _serviceContractList.Count == 0)
                {
                    return new ServiceResponse(1, "NLB Proxy does is not aware of any implemented contracts.", MessageType.Warn);
                }
                if (_proxyHost == null)
                {
                    _proxyHost = new ProxyServiceHostBase(SystemServiceType.SLBProxy, typeof(DiscoveryProxyService), new Type[] { typeof(IAdminSLBContract) });
                    _proxyHost.Open();
                }
                Trace.WriteLine(string.Format("NLB DiscoveryProxy listening on '{0}'...", _proxyHost.Contracts[0].ListenUri));
                Trace.WriteLine("");
                return new ServiceResponse();
            }
            catch (Exception ex)
            {
                return new ServiceResponse(2, ex.ToString(), MessageType.Error);
            }
        }

        private ServiceResponse RestartServices()
        {
            ServiceResponse response = StopProxyService();
            if (response.Code != 0)
            {
                return response;
            }
            return SetProxyService();
        }
    }
}
